Imports System.Runtime.InteropServices
Imports System.ComponentModel
Imports Microsoft.Win32

Public Class FileManagerViewPresenter

    Public Const STR_WebServicePath As String = "/DesktopModules/IWeb/webservice.asmx"

    Private Const STR_AllFilesFilter As String = "All Files (*.*)|*.*"
    Private Const STR_AboutVersionPattern As String = "IWeb Version: {0}"
    Private Const STR_NewFolderDefaultName As String = "New Folder"

    Private Const Setting_MaxUploads As Integer = 10
    Private Shared currentUploads As Integer = 0

    Private Const Setting_MaxDownloads As Integer = 10
    Private Shared currentDownloads As Integer = 0

    Private iconCtrl As New IconController

    Private WithEvents fileCtrl As New FileController

    Public Sub New(ByVal shell As MainForm, ByVal view As FileManagerView)

            Me.Shell = shell
            Me.View = view

            Me.Shell.DownloadFileDialog.InitialDirectory = Me.GetDownloadFolder

        Me.SetUpListViewColumns()


    End Sub


    Friend Sub SetConnection(ByRef result As Object)

        header = New IWeb.IWebAuthendicationHeader

        If String.IsNullOrEmpty(My.Settings.WebServiceAddress) Then
            result = "No Webservice address configured."
            Return
        End If

        If String.IsNullOrEmpty(My.Settings.Password) Then
            result = "Blank password is not allowed."
            Return
        End If


        Dim pass As String = My.Settings.Password


        Dim protocol As String

        If My.Settings.Https Then

            protocol = "https://"

        Else

            protocol = "http://"

        End If


        My.WebServices.WebService.Url = protocol & My.Settings.WebServiceAddress & STR_WebServicePath

        If My.Settings.Encrypted Then

            Dim encryptor As New IWebEncryption

            pass = encryptor.Encryption(pass, My.Settings.Key)

        End If

        header.Username = My.Settings.Username
        header.Encrypted = My.Settings.Encrypted.ToString
        header.Password = HttpUtility.UrlEncode(pass)
        header.PortalID = Convert.ToInt32(My.Settings.PortalId)

        header.ModuleId = -1

        Try

            Using service As New IWeb.WebService

                service.IWebAuthendicationHeaderValue = header

                Dim version As String = service.GetVersion

                Dim regExMatch As Boolean = System.Text.RegularExpressions.Regex.IsMatch(version, _
                    "[0-9]{2}\.[0-9]{2}\.[0-9]{2}", System.Text.RegularExpressions.RegexOptions.Compiled)

                If regExMatch Then

                    result = "Connected."

                    Return

                Else

                    result = "Web service connection is not properly configured."

                    Return

                End If

            End Using

        Catch ex As Exception



            My.Application.Log.WriteException _
                (ex, TraceEventType.Error, "Web service connection is not properly configured.")

            result = "Web service connection is not properly configured."

            Return
        End Try

    End Sub


#Region "Properties"


    Private viewField As FileManagerView
    Private shellField As MainForm

    Public Property Shell() As MainForm
        Get
            Return shellField
        End Get
        Set(ByVal value As MainForm)
            shellField = value
        End Set
    End Property


    Public Property View() As FileManagerView
        Get
            Return viewField
        End Get
        Set(ByVal value As FileManagerView)
            viewField = value
        End Set
    End Property
#End Region

#Region "Async Methods"

    Friend Sub BeginAddFolder(ByVal newFolder As String)

        Me.fileCtrl.AddNewFolderAsync _
            (New NewFolderEventArgs(Me.GetHeader, Me.GetSelectedFolderId, newFolder))

    End Sub

    Friend Sub BeginDeleteSelectedFiles()

        Me.fileCtrl.DeleteFilesAsync _
            (New DeleteFilesEventArgs(Me.GetHeader, Me.GetSelectedFileIds.ToArray))

    End Sub

    Friend Sub BeginDownloadFiles(ByVal fileId As Integer, ByVal fileName As String)


        Me.fileCtrl.DownloadFileAsync(New DownloadFileEventArgs(Me.GetHeader, fileId, fileName))

    End Sub

    Friend Sub BeginGetInfo()

        Dim args As New GetFileInfoEventArgs(Me.GetHeader, -1, Me.GetSelectedFileId)

        args.Header = Me.GetHeader

        Me.fileCtrl.GetFileInfoAsync(args)

    End Sub

    Friend Sub BeginGetChildFolders(ByVal node As TreeNode)


        Me.fileCtrl.GetFolderListAsync _
            (New GetFoldersEventArgs(Me.GetHeader, Convert.ToInt32(node.Tag)))


    End Sub

    Friend Sub BeginGetFilesByFolder(ByVal folderId As Integer)

        Me.fileCtrl.GetFileListAsync(New GetFilesEventArgs(Me.GetHeader, folderId))


    End Sub

    Friend Sub BeginUploadFiles(ByVal folderId As Integer, ByVal fileName As String)


        Me.fileCtrl.UploadFileAsync(New FileTransferEventArgs _
            (Me.GetHeader, folderId, fileName))


    End Sub


#End Region

#Region "Event Handlers"


    Private Sub fileCtrl_FileDownloading(ByVal sender As Object, ByVal e As DownloadFileEventArgs) Handles fileCtrl.FileDownloading

        currentDownloads += 1

        Me.View.DownloadBackgroundWorker.ReportProgress(0, e)

    End Sub

    Private Sub fileCtrl_FileDownloaded(ByVal sender As Object, ByVal e As DownloadFileEventArgs) Handles fileCtrl.FileDownloaded

        currentDownloads -= 1

        Me.View.DownloadBackgroundWorker.ReportProgress(0, e)


    End Sub

    Private Sub fileCtrl_FileInfoReceived(ByVal sender As Object, ByVal e As GetFileInfoEventArgs) Handles fileCtrl.FileInfoReceived

        Me.SetStatus(e)

        If Not e.HasError Then

            Dim info As IWeb.IWebFileInfo = e.File

            My.Forms.FilePropertiesDialog.Text = String.Format("{0} Properties", info.FileName)

            My.Forms.FilePropertiesDialog.FileNameLabel.Text = info.FileName

            My.Forms.FilePropertiesDialog.LastModifiedValueLabel.Text = info.LastModified.ToString


            My.Forms.FilePropertiesDialog.IconPictureBox.Image = New IconController().GetImageByExt(info.Extension)

            My.Forms.FilePropertiesDialog.Show()

        End If

    End Sub

    Private Sub fileCtrl_FilesDeleted(ByVal sender As Object, ByVal e As DeleteFilesEventArgs) Handles fileCtrl.FilesDeleted

        If e.HasError Then

            If MessageBox.Show(e.Status, "Error Deleteing File", _
                MessageBoxButtons.RetryCancel, MessageBoxIcon.Error, _
                MessageBoxDefaultButton.Button2) = DialogResult.Retry Then

                Me.BeginDeleteSelectedFiles()

            End If

        Else

            Me.SetStatus(e)

            Me.RefreshFiles()

        End If

    End Sub

    Private Sub fileCtrl_FilesReceived(ByVal sender As Object, ByVal e As GetFilesEventArgs) Handles fileCtrl.FileListReceived

        Me.SetStatus(e)

        Me.AddFilesToListView(e.Files)

    End Sub


    Private Sub fileCtrl_FileUploading(ByVal sender As Object, ByVal e As IWebExplorer.FileTransferEventArgs) Handles fileCtrl.FileUploading

        currentUploads += 1

        Me.View.UploadBackgroundWorker.ReportProgress(0, e)


    End Sub

    Private Sub fileCtrl_FileUploaded(ByVal sender As Object, ByVal e As IWebExplorer.FileTransferEventArgs) Handles fileCtrl.FileUploaded

        currentUploads -= 1

        Me.View.UploadBackgroundWorker.ReportProgress(0, e)


    End Sub

    Private Sub fileCtrl_FolderCreated(ByVal sender As Object, ByVal e As NewFolderEventArgs) Handles fileCtrl.FolderCreated

        Me.SetStatus(e)

    End Sub

    Private Sub fileCtrl_FoldersReceived(ByVal sender As Object, ByVal e As GetFoldersEventArgs) Handles fileCtrl.FolderListReceived

        Me.SetStatus(e)

        If Not e.HasError Then


            Dim folderId As Integer = e.FolderId

            If Me.View.FolderTreeView.Nodes.Count > 0 Then

                Dim parentNode As TreeNode = GetTreeNodeByFolderId _
                    (Me.View.FolderTreeView.Nodes(0), folderId)

                Me.AddChildFolderNodes(parentNode, e.Folders)

            Else

                Me.View.FolderTreeView.Nodes.Clear()

                Dim rootNode As New TreeNode
                rootNode.Tag = -1
                rootNode.Text = "Root"
                rootNode.ImageKey = "Folder"

                Me.View.FolderTreeView.Nodes.Add(rootNode)

                Me.AddChildFolderNodes(rootNode, e.Folders)


            End If


        End If

    End Sub



#End Region

#Region "UI Methods"

    Friend Sub ListPortals()
        Me.View.FileListView.Items.Clear()

        Using fileManager As New IWeb.WebService

            fileManager.IWebAuthendicationHeaderValue = GetHeader()

            Me.View.FileListView.Items.Add(fileManager.PortalsOnServer)

        End Using
    End Sub

    Friend Sub ShowVersion()
        Using service As New IWeb.WebService()

            service.IWebAuthendicationHeaderValue = GetHeader()

            My.Forms.About.IWebVersionLabel.Text = String.Format(STR_AboutVersionPattern, service.GetVersion)

            My.Forms.About.ShowDialog()

        End Using
    End Sub

    Private Sub AddFilesToListView(ByVal files As List(Of IWeb.IWebFileInfo))
        Me.View.FileListView.Items.Clear()


        Dim lvItem As ListViewItem

        For Each file As IWeb.IWebFileInfo In files

            If Not Me.View.SmallImageList.Images.ContainsKey(file.Extension) Then

                Me.View.SmallImageList.Images.Add(file.Extension, Me.iconCtrl.GetImageByExt(file.Extension))

                Me.View.LargeImageList.Images.Add(file.Extension, Me.iconCtrl.GetImageByExt(file.Extension))
            End If




            lvItem = Me.View.FileListView.Items.Add(file.FileName, file.Extension)

            lvItem.Tag = file.FileId

            lvItem.SubItems.AddRange(New String() _
                {file.Extension, file.ContentType, _
                file.Size.ToString, file.Height.ToString, file.Width.ToString})

        Next
    End Sub


    Friend Sub AddNewFolderItem()
        Dim item As New ListViewItem

        item.Text = STR_NewFolderDefaultName

        Me.View.FileListView.LabelEdit = True

        Me.View.FileListView.Items.Add(item)

        item.BeginEdit()

    End Sub


    Friend Function GetDownloadFolder() As String


        If String.IsNullOrEmpty(lastDownloadFolder) Then

            lastDownloadFolder = IO.Path.Combine _
                    (My.Computer.FileSystem.SpecialDirectories.MyDocuments, "IWebExplorer")

        End If

        IO.Directory.CreateDirectory(lastDownloadFolder)

        Return lastDownloadFolder

    End Function


    Friend Sub PopulateRootFolders()
        Me.View.FolderTreeView.Nodes.Clear()

        Dim rootNode As New TreeNode
        rootNode.Tag = -1
        rootNode.Text = "Root"
        rootNode.ImageKey = "Folder"

        Me.View.FolderTreeView.Nodes.Add(rootNode)

        Try
            Me.BeginGetChildFolders(rootNode)

            Me.BeginGetFilesByFolder(-1)

        Catch ex As Exception

            My.Application.Log.WriteException(ex, TraceEventType.Error, "PopulateRootFolders")

            Throw New InvalidOperationException("Could not retrieve a list of files and folders.", ex)

        End Try

    End Sub


    Friend Sub ReorderFileList(ByVal e As System.Windows.Forms.ColumnClickEventArgs)
        'ToDo Handle reverse ordering
        Dim sortedItems As New SortedDictionary(Of String, ListViewItem)

        For Each item As ListViewItem In Me.View.FileListView.Items

            If e.Column = ColumnNames.Name Then
                sortedItems.Add(item.Text, item)
            Else
                sortedItems.Add(item.SubItems.Item(e.Column).Text & "-" & item.Text, item)
            End If

        Next

        Me.View.FileListView.Items.Clear()

        For Each key As String In sortedItems.Keys

            Me.View.FileListView.Items.Add(sortedItems.Item(key))

        Next
    End Sub


    Friend Sub ToggleFoldersVisible()

        Me.Shell.FoldersToolStripMenuItem.Checked = Not Me.Shell.FoldersToolStripMenuItem.Checked

        Me.Shell.FoldersToolStripButton.Checked = Me.Shell.FoldersToolStripMenuItem.Checked

        Me.View.SplitContainer.Panel1Collapsed = Not Me.Shell.FoldersToolStripMenuItem.Checked

    End Sub

    Friend Sub ShowFileContextMenu(ByVal position As Drawing.Point)

        If Me.View.FileListContextMenuStrip.Items.Count = 0 Then

            Me.View.FileListContextMenuStrip.Items.Add("Add Folder")

        End If

        Me.View.FileListContextMenuStrip.Show(Me.View.FileListView, position)


    End Sub

    Friend Sub SetUpListViewColumns()


        With Me.View.FileListView.Columns
            .Clear()
            .Add("Name")
            .Add("Extension")
            .Add("Type")
            .Add("Size")
            .Add("Height")
            .Add("Width")
        End With


        SetView(System.Windows.Forms.View.Details)

    End Sub

    Friend Sub SetView(ByVal View As System.Windows.Forms.View)

        Dim MenuItemToCheck As ToolStripMenuItem = Nothing
        Select Case View
            Case View.Details
                MenuItemToCheck = Me.Shell.DetailsToolStripMenuItem
            Case View.LargeIcon
                MenuItemToCheck = Me.Shell.LargeIconsToolStripMenuItem
            Case View.List
                MenuItemToCheck = Me.Shell.ListToolStripMenuItem
            Case View.SmallIcon
                MenuItemToCheck = Me.Shell.SmallIconsToolStripMenuItem
            Case View.Tile
                MenuItemToCheck = Me.Shell.TileToolStripMenuItem
            Case Else
                Debug.Fail("Unexpected View")
                View = View.Details
                MenuItemToCheck = Me.Shell.DetailsToolStripMenuItem
        End Select

        For Each MenuItem As ToolStripMenuItem In Me.Shell.ListViewToolStripButton.DropDownItems
            If MenuItem Is MenuItemToCheck Then
                MenuItem.Checked = True
            Else
                MenuItem.Checked = False
            End If
        Next

        Me.View.FileListView.View = View
    End Sub


    Friend Sub RefreshFiles()

        If Me.View.FolderTreeView.SelectedNode IsNot Nothing Then

            Me.BeginGetFilesByFolder(Me.GetSelectedFolderId)

        Else

            Me.BeginGetFilesByFolder(-1)

        End If
    End Sub


    Friend Sub SetStatus(ByVal e As FileManagerEventArgs)

        If e.HasError Then

            My.Application.Log.WriteEntry(e.Status, TraceEventType.Error)

            MessageBox.Show(e.Status.Substring(0, 400), "An Error Occured.", _
                MessageBoxButtons.OK, MessageBoxIcon.Error, _
                MessageBoxDefaultButton.Button1)

        End If

        If Not String.IsNullOrEmpty(e.Status) Then

            If e.Status.Length > 75 Then

                Me.Shell.ToolStripStatusLabel.Text = e.Status.Substring(0, 75) & "..."

            Else
                Me.Shell.ToolStripStatusLabel.Text = e.Status

            End If

            My.Forms.StatusDialog.StatusTextBox.Text &= e.Status & vbCrLf

            My.Forms.StatusDialog.StatusTextBox.Select(My.Forms.StatusDialog.Text.Length - 1, 0)



        End If


    End Sub

    Friend Sub DownloadFiles(ByVal files As Generic.Dictionary(Of Integer, String))

        If currentDownloads >= Setting_MaxDownloads Then

            MessageBox.Show("Maximum number of downloads already in progress " & _
                "please wait until current downloads complete and try again.", _
                "Maximum Downloads", MessageBoxButtons.OK, MessageBoxIcon.Warning)

            Exit Sub

        End If


        For Each item As Generic.KeyValuePair(Of Integer, String) In files


            Do Until currentDownloads < Setting_MaxDownloads

                Threading.Thread.Sleep(10)

            Loop


            Me.fileCtrl.DownloadFileAsync _
                (New DownloadFileEventArgs(Me.GetHeader, item.Key, item.Value))


        Next



    End Sub

    Friend Sub UploadFiles(ByVal folderId As Integer, ByVal fileNames As List(Of String))

        If currentUploads >= Setting_MaxUploads Then

            Throw New InvalidOperationException("Maximum number of uploads already in progress " & _
                "please wait until current uploads complete and try again.")

            Exit Sub

        End If


        For Each filename As String In fileNames

            Do Until currentUploads < Setting_MaxUploads

                Threading.Thread.Sleep(10)

            Loop


            Me.BeginUploadFiles(folderId, filename)

        Next



    End Sub

#End Region

#Region "Private Methods"

    Private Enum ColumnNames
        Name = 0
        Extension = 1
        Type = 2
        Size = 3
        Height = 4
        Width = 5
    End Enum

    Private Shared header As IWeb.IWebAuthendicationHeader
    Private Shared lastDownloadFolder As String = String.Empty


    Private Sub AddChildFolderNodes(ByVal node As TreeNode, ByVal folders As List(Of IWeb.IWebFolderInfo))
        node.Nodes.Clear()

        If Not Me.View.SmallImageList.Images.ContainsKey("folder") Then

            Me.View.SmallImageList.Images.Add("folder", Me.iconCtrl.GetImageByExt("folder"))

        End If

        For Each folder As IWeb.IWebFolderInfo In folders


            Dim thisNode As New TreeNode()
            thisNode.Tag = folder.FolderId
            thisNode.Text = folder.FolderName
            thisNode.ImageKey = "folder"

            node.Nodes.Add(thisNode)

        Next
    End Sub


    Private Function GetHeader() As IWeb.IWebAuthendicationHeader

        If header Is Nothing Then

            MessageBox.Show("No web service connection is configured.")

            My.Forms.LoginForm.Show()

            Return Nothing

        End If

        Return header

    End Function


    Private Function GetSelectedFolderId() As Integer
        If Me.View.FolderTreeView.SelectedNode IsNot Nothing Then

            Return Convert.ToInt32(Me.View.FolderTreeView.SelectedNode.Tag)

        Else
            Return -1

        End If

    End Function

    Private Function GetSelectedFileId() As Integer

        If Me.View.FileListView.SelectedItems.Count <> 1 Then

            MessageBox.Show("Please select only one file for this operation.")

            Return -1

        Else

            Return Convert.ToInt32(Me.View.FileListView.SelectedItems(0).Tag)

        End If

    End Function

    Private Function GetSelectedFileIds() As List(Of Integer)

        Dim ids As New List(Of Integer)

        For Each item As ListViewItem In Me.View.FileListView.SelectedItems

            ids.Add(Convert.ToInt32(item.Tag))

        Next

        Return ids

    End Function

    Private Function GetTreeNodeByFolderId(ByVal startingNode As TreeNode, ByVal folderId As Integer) As TreeNode

        If folderId > -1 Then


            Dim parentNode As TreeNode = Nothing


            For Each node As TreeNode In startingNode.Nodes

                If Convert.ToInt32(node.Tag) = folderId Then

                    parentNode = node

                    Exit For

                ElseIf node.Nodes.Count > 0 Then

                    parentNode = Me.GetTreeNodeByFolderId(node, folderId)

                    If parentNode IsNot Nothing Then Exit For

                End If

            Next

            Return parentNode

        Else

            Return startingNode

        End If
    End Function


#End Region

End Class
